home *** CD-ROM | disk | FTP | other *** search
/ Fritz: All Fritz / All Fritz.zip / All Fritz / FILES / COMMADIO / KERMIT1.LZH / KERMIT.C < prev    next >
Text File  |  1980-01-01  |  35KB  |  1,039 lines

  1.  
  2. /*@ptxt[This is a minimal version of Kermit written in C.  It has many
  3. limitations, but does most of what is necessary.  It has no SET commands, no
  4. command parser, does only ASCII file transmission (no eight-@|bit or @q<.EXE>
  5. files) and some system-@|dependent routines are not filled in (like
  6. @q<gnxtfl()> for multiple sends), and does not implement any of the server
  7. functions.  This is intended mainly as an example, but it also demonstrates how
  8. a minimal version can be implemented.  Any command parser that will parse the
  9. four basic commands would be sufficient.
  10.   @Section<Symbols and Global Variables>
  11.  These are all the global variables and definitions.] */
  12.  
  13.  
  14. #include "c:stdio.h"
  15.  
  16. #define    maxpack    94        /* Maximum packet size */
  17.  
  18. #define    SOH    1        /* Start of header */
  19. #define    SP    32        /* ASCII space */
  20. #define    CR    13        /* Carriage return */
  21. #define    DEL    127        /* Delete (rubout) */
  22.  
  23. #define    maxtry    5        /* Times to retry a packet */
  24.  
  25. #define    myquote    '#'        /* Quote character I will use */
  26. #define    mypad    0        /* Number of padding characters I need */
  27. #define    mypchar    0        /* Padding character I need */
  28. #define    myeol    CR        /* End-Of-Line character I need */
  29. #define mytime    10        /* Seconds after which I should be timed out */
  30.  
  31. /* Declarations of global variables:  */
  32.  
  33. int
  34.  size,                /* Size of present data */
  35.  n,                /* Message number */
  36.  rpsiz,                /* Maximum receive packet size */
  37.  spsiz,                /* Maximum send packet size */
  38.  pad,                /* How much padding to send */
  39.  timint,            /* Timeout for foreign host on sends */
  40.  numtry,            /* Times this packet retried */
  41.  oldtry;            /* Times previous packet retried */
  42.  
  43. char
  44.  state,                /* Present state of the automaton */
  45.  padchar,            /* Padding character to send */
  46.  eol,                /* End-Of-Line to send */
  47.  quote,                /* Quote character in incoming data */
  48.  filnam[50],            /* The file name */
  49.  recpkt[maxpack],        /* Receive packet buffer */
  50.  packet[maxpack];        /* Packet buffer */
  51.  
  52. FILE *fptr;            /* File pointer
  53.  
  54. @End<ProgramExample>
  55. @Section<Command Parsing>
  56. @Index[Parsing Commands]@Index[Prompt]
  57.  The parsing depends upon the capabilities of the host, but the
  58. following syntax is recommended for uniformity:
  59. The prompt should be "@q{Kermit-XXX>}" where @q<XXX> denotes the
  60. implementation, e.g. "@q{Kermit-20>}", "@q{Kermit-80>}", etc.  It's important
  61. to have distinct prompts to avoid confusion while the user talks to two
  62. different Kermits from the same terminal.
  63.  
  64. Commands should be a keyword followed by arguments.  Optional arguments
  65. are shown in square brackets.
  66.  
  67. @SubSection<Required Commands>
  68. @begin<example>
  69. SEND <filename or filegroup>
  70. RECEIVE [<filename>]
  71. HELP
  72. EXIT
  73. ?
  74. @end<example>
  75. "@q<?>" should simply list the commands or arguments possible at that point.
  76.  
  77. File specifications conform to the requirements of the specific system.
  78. Most systems allow the specification of a file group by the inclusion of
  79. special "wildcard" or pattern-matching characters in the file
  80. specification.  A list of files, or file groups, may also be allowed.
  81.  
  82. For SEND or RECEIVE, parse and store the file (group) name.
  83. If RECEIVE, the file name is optional, since it can be built from
  84. the name provided in the file header sent by the foreign host.
  85. Including a file name on the receive command implies that the user wants to
  86. store the file under a different name than it was sent with.  This could be
  87. useful if the two hosts have different file name formats, but should not be
  88. done when receiving file groups.
  89.  
  90. HELP types a help message at the terminal, EXIT terminates execution of
  91. Kermit.
  92.  
  93. @SubSection<Optional Commands>
  94. @begin<example>
  95. @tabclear()@tabdivide(3)
  96. SET <parameter>@\@i(e.g. padding, packet size, etc.)
  97. SHOW <parameter>@\@i(anything that can be SET)
  98. STATUS@\@i(e.g. of connection) 
  99. CONNECT@\@i(to remote host)
  100. SERVER@\@i(act as a server)
  101. PROMPT@\@i(run interactively)
  102. QUIT@\@i(equivalent to) EXIT
  103.  @end<example>
  104.  
  105. @Index[Virtual Terminal]@Index[Connect]@Index[Escape Character]
  106.  CONNECT establishes a "virtual terminal" connection to the remote
  107. host.  It is mainly useful on microcomputers with serial i/o ports
  108. which can be plugged in as terminals to the remote host.  An escape
  109. mechanism must be provided to get the user back to the local host;
  110. preferably a two-@|character sequence, in which the first character
  111. (normally control-@|rightbracket) gets the attention of the local host and
  112. the second tells it what to do, for example:
  113.  @begin<description,spread 0,leftmargin +4,indent -4>
  114. @q<C>@\Close the connection, return to local host.
  115.  
  116. @q<S>@\Show the status of the connection.
  117.  
  118. @q<B>@\Send a BREAK character.
  119.  
  120. @q<^]>@\Send a control-rightbracket (or whatever the escape character is).
  121.  
  122. @q<X>@\Go into extended command mode, e.g. allowing SET commands.
  123.  
  124. @q<?>@\List the available options
  125.  @end<description>
  126.  
  127. @Index[Set]
  128. SET should allow establishment of any system-@|dependent parameters.
  129. Here is the suggested syntax:
  130.  
  131. @begin<Example,group>
  132.     | SEND    |  | PACKET-LENGTH <d> |
  133. SET |         |  | PADDING <d>       |
  134.     | RECEIVE |  | PADCHAR <o>       |
  135.                  | TIMEOUT <d>       |
  136.                  | END-OF-LINE <o>   |
  137.                  | QUOTE <c>         |
  138.  
  139.     | DEBUGGING    |  | ON  |
  140.     | FILE-WARNING |  | OFF |
  141.     | LOCAL-ECHO   |
  142.  
  143.     LINE <n>
  144.     ESCAPE <o>
  145.     DELAY <d>
  146. @end<Example>
  147.  where @q(<d>) is a decimal number in the range 0-94, @q(<o>) is an octal
  148. number (0-37), @q(<c>) is a single printable character, and @q(<n>) is a
  149. number in whatever radix the system expresses terminal numbers
  150. in@foot<Some systems might have other ways of identifying
  151. communication lines, such as character strings.>.  The
  152. LINE specification would let Kermit communicate over another terminal port
  153. on the same system, rather than its own controlling terminal.
  154.  
  155. The following SET parameters are recommended for an implementation of
  156. Kermit that wants to be completely general, i.e. to communicate with as
  157. many different machines as possible:
  158.  @begin<description>
  159. @Index[Packet-Length]
  160. PACKET-LENGTH@\The maximum packet length for either outgoing or incoming
  161. packets.  The maximum packet size can be no bigger than 96 (decimal),
  162. but may have to be smaller than that if the host has trouble buffering
  163. packets of that size (especially at higher baud rates).
  164.  
  165. @Index[Timeout]
  166. TIMEOUT@\Specify the timeout interval for the outgoing (SEND) and incoming
  167. (RECEIVE) Kermits.
  168.  
  169. @Index[Padding] 
  170. PADDING@\The user should be able to specify the padding on outgoing
  171. packets (so the first send-init packet can get through) and on
  172. incoming packets (so the local Kermit can inform the remote Kermit of
  173. its own padding requirements).  The argument specifies the number of pad
  174. characters with which to precede each packet.
  175.  
  176. PADCHAR@\The character to use for padding.  This should default to NUL,
  177. but some systems want others; e.g. IBM uses DEL.
  178.  
  179. @Index[End-Of-Line (EOL)]
  180. END-OF-LINE@\The user should be able to specify the line terminator for
  181. outgoing packets, so the first @q<send-init> packet can get through.  If the
  182. user knows that the remote Kermit will use a different terminator than the
  183. one the local Kermit expects, then that can be set too, though the
  184. appropriate default should be hardwired into the program and sent to the
  185. remote Kermit in the @q<send-init> packet.
  186.  
  187. @Index[Quote] 
  188. QUOTE@\The user might need to change the quote character on outgoing
  189. packets in case the default quote character is causing problems at the
  190. other end.
  191.  
  192. @Index[Eight-Bit-Quote]
  193. EIGHT-BIT-QUOTE@\The character to be used for quoting bytes with the 8th bit
  194. set, for systems that cannot transmit or receive 8-bit ASCII.
  195.  
  196. @Index[Escape Character] 
  197. ESCAPE@\If a CONNECT command is provided, this allows the default escape
  198. character (Control-@q<]>) to be replaced by the one specified.
  199.  
  200. @Index[Local-Echo]
  201. LOCAL-ECHO@\For CONNECTing to a half-@|duplex host.
  202.  
  203. @Index[Parity]
  204. PARITY@\Specify parity on outgoing packets -- NONE, SPACE, MARK, EVEN, or ODD.
  205. If NONE, then the 8th bit can be used for data; all the others usurp the 8th
  206. bit.  If other than NONE, the parity bit is ignored on incoming characters.
  207.  
  208. @Index[IBM-Mode]
  209. IBM-MODE@\For communicating with an IBM mainframe.  Makes the
  210. local Kermit wait for the half duplex line to turn around (i.e.@ wait for the
  211. receipt of an XON character), and has the local Kermit always send whatever
  212. parity the IBM front ends insist upon.
  213.  
  214. @Index[Delay] 
  215. DELAY@\Allow the user to specify a delay before sending the @q<send-init>
  216. packet.  If the remote Kermit is sending, this gives the user time to
  217. get back and start up the local receiving Kermit in time to catch the
  218. first incoming packet.
  219.  
  220. @Index[File-Warning]
  221. FILE-WARNING@\When receiving files, allow the user to elect whether to
  222. be warned if a filename conflict will occur, i.e. if Kermit is about
  223. to receive a file that has the same name as one already on the disk.
  224. If the user elects to be warned, Kermit should allow the user to
  225. specify new names when conflicts arise.
  226.  
  227. @Index[Debugging] 
  228. DEBUGGING@\Allow the user to see packets as they go by, to receive extra
  229. informative and error messages, and to have some extra controls over the
  230. operation of the program.
  231.  @end<description>
  232.  @Begin<ProgramExample>
  233.  @Section<Main>
  234. /*@ptxt[This is the main routine.  It should parse for the parameters
  235.    specified in the previous section and call the appropriate
  236.    routine (@q<sendsw> for send and @q<recsw> for receive.)]  */
  237.  
  238. main ()
  239.  
  240. {
  241.     if(sendsw() == FALSE) printf("Sending failed\n");
  242.     else printf("Sending suceeded\n");
  243. }
  244.  
  245. /*@ptxt[The main routine is left to the future implementer to fill in.]
  246. @End<ProgramExample>
  247. @Section<Send-switch>
  248. @Begin<ProgramExample>
  249. /*@ptxt[Sendsw is the state table switcher for sending files.  It loops
  250.    until either it is finished or an error is encountered.  The
  251.    routines called by sendsw are responsible for changing the state.]
  252. */
  253.  
  254. sendsw ()
  255.  
  256. {
  257.     state = 'S';        /* Send initiate is the start state */
  258.     n = 0;            /* Initialize message number */
  259.     numtry = 0;            /* Say no tries yet */
  260.     while (TRUE)            /* Do this for as long as necessary */
  261.     {
  262.     printf("State: %c\n",state);    /* Temp for testing */
  263.     switch (state)
  264.     {
  265.         case 'D':  state = sdata(); break;    /* Data-Send state */
  266.         case 'F':  state = sfile(); break;    /* File-Send */
  267.         case 'Z':  state = seof(); break;    /* End-of-File */
  268.         case 'S':  state = sinit(); break;    /* Send-Init */
  269.         case 'B':  state = sbreak(); break;    /* Break-Send */
  270.         case 'C':  return(TRUE);        /* Complete */
  271.         case 'A':  return(FALSE);        /* Abort */
  272.         default:   return(FALSE);        /* Unknown, abort */
  273.     }
  274.     }
  275. }                        /* Finished.
  276. @End<ProgramExample>
  277. @SubSection<send-init>
  278. @Begin<ProgramExample>
  279.  
  280. /*@ptxt[This routine initializes the connection with the foreign host.
  281.    sinit is the prototype for all packet sending routines.  It calls  
  282.    spack to send a correctly formatted Send-Initiate packet with
  283.    the necessary data for initialization (packet size, timeouts, etc).
  284.    The foreign host will ACK with a packet containing its own init
  285.    data: how long a packet it can handle, when to time it out, etc.  A
  286.    dumb host can ignore the timeout data; hopefully the other host is
  287.    smart and can handle the timeouts.  If any of the following occur,
  288.    numtry is incremented and the routine returns without changing
  289.    state (this will happen at most maxtry times after which the state
  290.    changes to Abort):
  291. @begin(itemize,spread 0)
  292.    A bad packet (@q<rpack> returns FALSE);
  293.  
  294.    A NAK is received;
  295.  
  296.    The wrong ACK is received.
  297. @end(itemize)
  298.    If the foreign host ACKs correctly, the values for packet size,
  299.    etc, should be taken from its ACK packet and the state changed to
  300.    Send-File (F).]
  301. */
  302.  
  303. char sinit ()
  304.  
  305. {
  306.     int num, len;            /* Local variables */
  307.  
  308. printf("sinit\n");
  309.     if (numtry++ > maxtry) return('A');    /* If too many tries, abort */
  310.     spar(packet);            /* Fill up with init info */
  311.     spack('S',n,6,packet);        /* Send an 'S' packet, */
  312.     switch (rpack(&len,&num,recpkt))    /* What was the reply? */
  313.     {
  314. /* NAK */
  315.     case 'N':
  316.         if (n != num--) return(state);    /* NAK: fail */
  317.                     /* If NAK for next pack its like ACK
  318.  
  319. /* ACKnowlegement */
  320.     case 'Y':
  321.     {
  322.         if (n != num) return(state);    /* If wrong ACK, fail */
  323.         rpar(recpkt);            /* Get the init info */
  324.         if (eol == 0) eol = '\n';        /* Check and set defaults */
  325.         if (quote == 0) quote = '#';
  326.         numtry = 0;                /* reset try counter, */
  327.         n = (n + 1) % 64;            /* and bump packet count */
  328.         fptr = fopen(filnam,"r");        /* Open the file to read */
  329.         if (fptr < 0) return('A');          /* Abort if can't */
  330.         return('F');            /* Switch state to 'F' */
  331.     }
  332.     case FALSE:  return(state);        /* Receive failure: fail */
  333.     default:     return('A');        /* Just "Abort" */
  334.     }
  335. }                        /* Return
  336. @End<ProgramExample>
  337. @SubSection<send-file>
  338. @Begin<ProgramExample>
  339.  
  340. /*@ptxt[This routine is almost the same as sinit except the data field
  341.    contains the name of the file being sent.  It reacts the same way
  342.    on all packets received except when a correct ACK is received.  In
  343.    that case the state is changed to 'D' (data send) and bufill is
  344.    called to fill up the buffer to send to the foreign host.] */
  345.  
  346. char sfile ()
  347.  
  348. {
  349.     int num, len;            /* Local variables */
  350.  
  351.     if (numtry++ > maxtry) return('A');    /* If too many tries, abort */
  352.     for(len=0;filnam[len] != '\0';len++);    /* Count the length */
  353.     len++;
  354.     spack('F',n,len,filnam);        /* Send an 'F' packet, */
  355.     switch (rpack(&len,&num,recpkt))    /* What was the reply? */
  356.     {
  357. /* NAK */
  358.     case 'N':
  359.         if (n != num--) return(state);    /* NAK: fail */
  360.                     /* If NAK for next pack it's
  361.                       like ACK, fall through
  362. /* ACKnowlegement */
  363.     case 'Y':
  364.     {
  365.         if (n != num) return(state);    /* If wrong ACK, fail */
  366.         numtry = 0;                /* Reset try counter, */
  367.         n = (n + 1) % 64;            /* and bump packet count */
  368.         size = bufill(packet);        /* Get the data from file */
  369.         return('D');            /* Switch state to 'D' */
  370.     }
  371.     case FALSE:  return(state);        /* Receive failure: fail */
  372.     default:     return('A')        /* Just "Abort" */;
  373.     }
  374. }                        /* Return
  375. @End<ProgramExample>
  376. @SubSection<send-data>
  377. @Begin<ProgramExample>
  378.  
  379. /*@ptxt[Another similar routine.  It sends a data packet containing the
  380.    contents of the data buffer.  On ACK it does the same as sfile.
  381.    When bufill returns EOF the state changes to 'Z' (EOF send).] */
  382.  
  383. char sdata ()
  384.  
  385. {
  386.     int num, len;            /* Local variables */
  387.  
  388.     if (numtry++ > maxtry) return('A');    /* If too many tries, abort */
  389.     spack('D',n,size,packet);        /* Send an 'D' packet, */
  390.     switch (rpack(&len,&num,recpkt))    /* What was the reply? */
  391.     {
  392. /* NAK */
  393.     case 'N':
  394.         if (n != num--) return(state);    /* NAK: fail */
  395.                     /* If NAK for next pack it's
  396.                       like ACK, fall through
  397.  
  398. /* ACKnowlegement */
  399.     case 'Y':
  400.     {
  401.         if (n != num) return(state);    /* If wrong ACK, fail */
  402.         numtry = 0;                /* Reset try counter, */
  403.         n = (n + 1) % 64;            /* and bump packet count */
  404.         if ((size = bufill(packet)) == EOF) return('Z');
  405.             /* Get the data from file; if EOF set state to it */
  406.         return('D');            /* Switch state to 'D' */
  407.     }
  408.     case FALSE:  return(state);        /* Receive failure: fail */
  409.     default:     return('A')        /* Just "Abort" */;
  410.     }
  411. }                        /* Return
  412. @End<ProgramExample>
  413. @SubSection<send-EOF>
  414. @Begin<ProgramExample>
  415.  
  416. /*@ptxt[Sends an End-Of-File packet.  On ACK it calls gnxtfl.  If that
  417.    succeeds the state becomes 'F' (file send) again.  If it fails the
  418.    state becomes 'B' (break connection).]
  419. */
  420.  
  421. char seof ()
  422.  
  423. {
  424.     int num, len;            /* Local variables */
  425.  
  426.     if (numtry++ > maxtry) return('A');    /* If too many tries, abort */
  427.     spack('Z',n,0,packet);        /* Send an 'Z' packet, */
  428.     switch (rpack(&len,&num,recpkt))    /* What was the reply? */
  429.     {
  430. /* NAK */
  431.     case 'N':
  432.         if (n != num--) return(state);    /* NAK: fail */
  433.                     /* If NAK for next pack it's
  434.                       like ACK, fall through
  435.  
  436. /* ACKnowlegement */
  437.     case 'Y':
  438.     {
  439.         if (n != num) return(state);    /* If wrong ACK, fail */
  440.         numtry = 0;                /* Reset try counter, */
  441.         n = (n + 1) % 64;            /* and bump packet count */
  442.         if (gnxtfl() == EOF) return('B');    /* No more files go to Break */
  443.         return('F');            /* Switch state to 'F' */
  444.     }
  445.     case FALSE:  return(state);        /* Receive failure: fail */
  446.     default:     return('A')        /* Just "Abort" */;
  447.     }
  448. }                        /* Return
  449. @End<ProgramExample>
  450.  
  451. @SubSection<send-EOT>
  452. @Begin<ProgramExample>
  453. /*@ptxt[Sends a Break (End Of Transmission) packet.  On ACK the state
  454.    becomes 'C' (complete).]
  455. */
  456.  
  457. char sbreak ()
  458.  
  459. {
  460.     int num, len;            /* Local variables */
  461.  
  462.     if (numtry++ > maxtry) return('A');    /* If too many tries, abort */
  463.     spack('B',n,0,packet);        /* Send an 'B' packet, */
  464.     switch (rpack(&len,&num,recpkt))    /* What was the reply? */
  465.     {
  466. /* NAK */
  467.     case 'N':
  468.         if (n != num--) return(state);    /* NAK: fail */
  469.                     /* If NAK for next pack it's
  470.                       like ACK, fall through
  471.  
  472. /* ACKnowlegement */
  473.     case 'Y':
  474.     {
  475.         if (n != num) return(state);    /* If wrong ACK, fail */
  476.         numtry = 0;                /* Reset try counter, */
  477.         n = (n + 1) % 64;            /* and bump packet count */
  478.         return('C');            /* Switch state to 'C' */
  479.     }
  480.     case FALSE:  return(state);        /* Receive failure: fail */
  481.     default:     return('A')        /* Just "Abort" */;
  482.     }
  483. }                        /* Return
  484. @End<ProgramExample>
  485. @Section<I/O routines>
  486. @Begin<ProgramExample>
  487.  
  488. /*@ptxt[The following routines deal with the building, sending and
  489.    receiving of packets as well as the manipulation of files.
  490.    Their low level I/O routines tend to be system dependent.
  491.    The use of a high-level language such as C tends to insulate
  492.    the programmer from this.]
  493. @End<ProgramExample>
  494. @SubSection<Send-packet>
  495. @Begin<ProgramExample>
  496.  
  497. /*@ptxt[This routine builds and sends the packet according to the
  498.    specifications it is passed.  It computes the checksum.  The
  499.    packet is sent the best way the machine will allow.  It
  500.    assumes that any control characters in the data buffer are
  501.    already quoted, and the count includes the quote characters.
  502.  
  503.    Packet format (all numbers in octal):]
  504. @begin<verbatim,group>
  505.    @u<Field>          @ux<Chars Allowed>  @u<Length>    @u<Description>
  506.    start of packet  001-037        1       001 by definition
  507.    character count  040-176        1       0 to 136 + SP
  508.    message number   040-137        1       modulo 100 + SP
  509.    message type     040-176        1       mnemonic (see below)
  510.    data             040-176     count-3    0 to 132 chars
  511.    checksum         040-137        1       (sum+((sum & 300)/100) & 77
  512.  
  513.    Mnemonics for message type:
  514.  
  515.      D      data packet
  516.      Y      acknowledge
  517.      N      negative acknowledge
  518.      S      send initiate
  519.      B      break transmission
  520.      F      file header
  521.      Z      end of file
  522.      E      error
  523. @end<verbatim>
  524. */
  525.  
  526. spack (type,num,len,data)
  527.  
  528. char type, data[];
  529. int num, len;
  530.  
  531. {
  532.     int i;
  533.     char chksum;            /* Local variables
  534.  
  535. /* Initialize some parameters */
  536.  
  537. printf("spack\n");
  538.     for(i=1;i<=pad;i++) fputc(padchar,stdout);    /* Issue necessary padding */
  539.     fputc(SOH,stdout);            /* Packet marker, ASCII 1 (SOH)*/
  540.     chksum = tochar(len+3);        /* Initialize the checksum */
  541.     fputc(tochar(len+3),stdout);    /* Send the character count */
  542.     chksum = chksum + tochar(num);    /* Init checksum */
  543.     fputc(tochar(num),stdout);        /* Packet number */
  544.     chksum = chksum + type;
  545.     fputc(type,stdout);            /* Packet type
  546.  
  547.  
  548. /* Loop for all data characters */
  549.  
  550.     for(i=0;i<len;i++)
  551.     {
  552.     fputc(data[i],stdout);
  553.     chksum = chksum+data[i];
  554.     }
  555.     chksum = (chksum + (chksum & 192) / 64) & 63;
  556.     fputc(tochar(chksum),stdout);    /* Checksum */
  557.     fputc(eol,stdout);            /* Extra-packet line terminator */
  558. }
  559.  
  560. /*@ptxt[A system that could only do record (line) output would deposit each
  561.    character into a buffer and then send the buffer.]
  562. @End<ProgramExample>
  563. @SubSection<Receive-packet>
  564. @Begin<ProgramExample>
  565.  
  566. /*@ptxt[This is a routine which does the opposite of Send-packet. It
  567.    waits for characters to arrive from the foreign host.  Upon
  568.    receiving a SOH character it starts interpreting the ensuing
  569.    characters depending on their position and value.
  570.  
  571.    If it encounters another SOH before it comes to the end of the
  572.    packet it starts over with the new SOH as the beginning of the
  573.    packet.  If the packet is invalid in any way (bad checksum, invalid
  574.    message number, etc.)  the routine returns FALSE.  If the packet is
  575.    valid its type, message number, length of data and pointer to data
  576.    are set in in the appropriate global variables.]
  577. */
  578.  
  579. rpack (len,num,data)
  580.  
  581. int *len, *num;
  582. char *data;
  583.  
  584. {
  585.     int i, fld;
  586.     char chksum, t, type;        /* Local variables
  587.  
  588. printf("\nrpack\n");            /* For testing purposes */
  589.     chksum = 0;                /* Initialize checksum */
  590.     t = 0;                /* Initialize input char value */
  591.     while ((t=fgetc(stdin)) != SOH);    /* Wait for packet header
  592.  
  593. /* Got one, loop for each field */
  594.     for (fld=1;fld<=5;fld++)
  595.     {
  596.     if(fld != 5 || i != 0)        /* Don't get char on no data packets */
  597.         if((t=fgetc(stdin)) == SOH) fld = 0;    /* Resynch if SOH */
  598.     if(fld <= 3) chksum = chksum + t;    /* Accumulate checksum */
  599. printf("case: %d, checksum: %d\n",fld,chksum);
  600.     switch (fld)
  601.     {
  602.         case 0:  chksum = 0; break;        /* Restart loop */
  603.         case 1:  *len = unchar(t)-3; break;    /* Character count */
  604.         case 2:  *num = unchar(t); break;    /* Packet number */
  605.         case 3:  type = t; break;        /* Packet type */
  606.         case 4:  for(i=0;i<*len;i++)
  607.         {
  608.         if(i != 0)
  609.             if((t=fgetc(stdin)) == SOH) /* Get a char */
  610.             {
  611.             fld = -1;
  612.             break;
  613.             }
  614.         chksum = chksum + t;         /* Add it to the checksum */
  615. printf("i: %d, checksum: %d\n",i,chksum);
  616.         data[i] = t;            /* Normal character */
  617.         }
  618.              break;
  619.         case 5:  chksum = (chksum + (chksum & 192) / 64) & 63;
  620.              break;
  621.     }
  622.     }                        /* We now have all the chars
  623.  
  624. /* Check the checksum */
  625.     if(chksum != unchar(t))
  626.     {
  627.     printf("Bad checksum: %c, should be: %c\n",t,tochar(chksum));
  628.     return(FALSE);
  629.     }
  630.     return(type);                /* Return packet type */
  631. }
  632.  
  633. /*@ptxt[A system that has only line or record input would get the line into
  634.    an intermediate buffer and then pick it apart character by
  635.    character.]
  636. @End<ProgramExample>
  637. @SubSection<Buffer-fill>
  638. @Begin<ProgramExample>
  639.  
  640. /*@ptxt[Bufill gets the characters from the file fptr.  The characters are 
  641.    put into the buffer and any quoting of control characters is done
  642.    here.  As many characters as possible should be put into buffer,
  643.    but the buffer must not be longer than the receiving Kermit's buffer
  644.    size; spsiz minus 5.  The count of characters includes the quoting
  645.    characters.  When EOF is encountered as many characters as are left
  646.    should be put in the buffer.  The next time this routine is called
  647.    it should return failure to indicate EOF.
  648.  
  649.    All control characters (0-37 and 177) in the data field are
  650.    substituted by a quote character followed by the control character
  651.    plus 100 modulo 200, i.e. the result of transforming the character
  652.    via the ctl function.  The quote character is '#' by default, but
  653.    can be any value 41-76 or 140-176 specified by the host, though for
  654.    efficiency it should be a seldom used character.  Transmission of
  655.    the quote character itself is obtained by quoting it with itself.
  656.  
  657.    The number of characters put into the buffer is returned.]
  658. */
  659.  
  660. bufill (buffer)
  661.  
  662. char buffer[];
  663.  
  664. {
  665.     int i, t;                /* Local variables */
  666.  
  667.     i = 0;                /* Init data buffer pointer */
  668.     while ((t = fgetc(fptr)) != EOF)    /* Get the next character */
  669.     {
  670.     t = t & 0177;            /* Strip parity */
  671.     if(t < SP || t == DEL || t == quote)
  672.     {
  673.         buffer[i++] = quote;
  674.         if(t != quote) t = ctl(t);
  675.     }
  676.     buffer[i++] = t;
  677.     if(i >= spsiz-6) return(i);
  678.     }
  679.     if(i == 0) return(EOF);    /* Wind up here only on EOF */
  680.     return(i);
  681. }                /* So the partial buffer isn't lost
  682. @End<ProgramExample>
  683. @SubSection<Buffer-empty>
  684. @Begin<ProgramExample>
  685.  
  686. /*@ptxt[This is a system dependent routine that takes the characters from
  687.    the buffer and writes them to the file.  The ACK for the packet
  688.    containing the characters should not be given until the characters
  689.    are written.  If there is an error writing the file, particulary of
  690.    the "disk full" or "quota exceeded" type, send an appropriate error
  691.    packet (if a remote Kermit) and then a Break packet to terminate
  692.    transmission.]
  693. */
  694.  
  695. bufemp (buffer,filptr,len)
  696.  
  697. char buffer[];
  698. FILE *filptr;
  699. int len;
  700.  
  701. {
  702.     int i, t;
  703.  
  704.     for(i=0;i<len;i++)
  705.     {
  706.     t = buffer[i];
  707.     if(t == myquote)         /* Handle quoted chars */
  708.     {
  709.         t = buffer[++i];         /* Get the quoted char */
  710.         if(t != myquote) t = ctl(t); /* De-controlify the char */
  711.     }
  712.     fputc(t,filptr);         /* Put the char in the file */
  713.     }
  714. }                     /* Return
  715. @End<ProgramExample>
  716. @SubSection<Get-file>
  717. @Begin<ProgramExample>
  718.  
  719. /*@ptxt[This routine gets a file to output data to.  If the user specified
  720.    a file name in the RECEIVE command, that file is used.  If not, the
  721.    information in data supplied by the foreign host is used,
  722.    transformed if necessary to construct a legal file name.  If the
  723.    SET FILE-WARNING feature has been implemented and the user has
  724.    specified that it should be used, that name is looked up on the
  725.    disk to see if a file of that name already exists, and if so the
  726.    file is given another name and the user is informed of the new
  727.    name.  This command is not implemented in this version.  The file
  728.    is opened for output, and fptr becomes the file pointer for that file.
  729. ]
  730. */
  731.  
  732. getfil (filenm)
  733.  
  734. char *filenm;
  735.  
  736. {
  737.     if(filenm[0] == '\0') fptr = fopen(packet,"w");
  738.     else fptr = fopen(filenm,"w");
  739. }
  740. /* If user didn't specify a file use the one from other host. 
  741. @End<ProgramExample>
  742. @SubSection<Get-next-file>
  743. @Begin<ProgramExample>
  744.  
  745. /*@ptxt[This routine gets the next file in a file group if there is one.
  746.    If not it returns EOF.  In this implementation we only allow single
  747.    file transmission, so this routine always returns EOF.]
  748. */
  749.  
  750. gnxtfl ()
  751.  
  752. {
  753.     return(EOF);
  754. }            /* Don't do anything
  755. @End<ProgramExample>
  756. @Section<Receive-switch>
  757. @Begin<ProgramExample>
  758.  
  759. /*@ptxt[This routine is another state table switcher, like sendsw.
  760.    It loops until it is finished or an error is encountered.
  761.    The routines called by this one are responsible for changing
  762.    the state.]
  763. */
  764.  
  765. recsw ()
  766.  
  767. {
  768.     state = 'R';        /* Receive is the start state */
  769.     n = 0;            /* Initialize message number */
  770.     numtry = 0;            /* Say no tries yet */
  771.     while (TRUE) switch (state)    /* Do this for as long as necessary */
  772.     {
  773.     case 'D':  state = rdata(); break;    /* Data receive state */
  774.     case 'F':  state = rfile(); break;    /* File receive state */
  775.     case 'R':  state = rinit(); break;    /* Send initiate state */
  776.     case 'C':  return(TRUE);        /* Complete state */
  777.     case 'A':  return(FALSE);        /* Abort state */
  778.     default:   return(FALSE);        /* Unknown state, abort */
  779.     }
  780. }                        /* Finished.
  781. @End<ProgramExample>
  782.  
  783. @SubSection<Receive-init>
  784. @Begin<ProgramExample>
  785. /*@ptxt[This routine initializes the connection with the foreign host.  It
  786.    waits for the foreign host to initiatate the connection.  Upon
  787.    receipt of a send-initiate packet it sets its variables to what the
  788.    foreign host requested and calls spack to send an ACK packet with
  789.    the necessary data for initialization (packet size and time outs).
  790.    The state is then set to 'F' (File-receive).  If the packet
  791.    received is not a send initiate the state goes to abort.  If a
  792.    packet (receive-packet returns FALSE) is received the state is not
  793.    changed.]
  794. */
  795.  
  796. rinit ()
  797.  
  798. {
  799.     int len, num;            /* Local variables */
  800.  
  801.     if(numtry++ > maxtry) return('A');    /* "Abort" if too many tries */
  802.     switch (rpack(&len,&num,packet))    /* Get a packet */
  803.     {
  804.  
  805.     case 'S':            /* Send-Init - Get parameters */
  806.     {
  807.         n = num;            /* Synchronize packet numbers */
  808.         rpar(packet);        /* Get the init data */
  809.         spar(packet);        /* Fill up packet with init info */
  810.         spack('Y',n,6,packet);    /* ACK with my parameters */
  811.         oldtry = numtry;        /* Save old try count */
  812.         numtry = 0;            /* And start a new counter */
  813.         n = (n + 1) % 64;        /* Bump packet number */
  814.         return('F');        /* OK, now enter File-Send state */
  815.     }
  816.  
  817.     case FALSE:  return(state);    /* Oops, we didn't receive a packet */
  818.     default:     return('A');    /* Unknown type, "Abort" */
  819.     }
  820. }                    /* Return
  821. @End<ProgramExample>
  822. @SubSection<Receive-file>
  823. @Begin<ProgramExample>
  824.  
  825. /*@ptxt[The rest of the receive routines are similar to this one.  If the
  826.    expected packet is received (in this case an 'F' packet with the
  827.    correct message number) the packet is ACKed and the state changed
  828.    (in this case to 'D', Data-receive).
  829.  
  830.    In this routine, if no file name was specified by the user the one
  831.    supplied by the foreign host in the 'F' packet is used to build one.
  832.    If the packet is an 'S' or 'Z' packet with the
  833.    previous message number, the routine ACKs the previous packet again
  834.    and returns without changing state.  If a 'B' is received then
  835.    there are no more files and the state goes to 'C'.  If any other
  836.    packet is received the state goes to "Abort".]
  837. */
  838.  
  839. char rfile ()
  840.  
  841. {
  842.     int num, len;
  843.  
  844.     if(numtry++ > maxtry) return('A');    /* "Abort" if too many tries */
  845.     switch (rpack(&len,&num,packet))    /* Get a packet */
  846.     {
  847. /* Send-Init */
  848.     case 'S':
  849.     {
  850.         if(oldtry++ > maxtry) return('A');    /* If too many tries */
  851.         if(num == n - 1)            /* Check packet number */
  852.         {
  853.         spar(packet);            /* Get the init params */
  854.         spack('Y',num,6,packet);    /* It's right, ACK it */
  855.         numtry = 0;            /* Reset try counter */
  856.         return(state);
  857.         }
  858.         else return('A');            /* Sorry, wrong number */
  859.     }                    /*
  860.  
  861. /* End-of-File */
  862.     case 'Z':
  863.     {
  864.         if(oldtry++ > maxtry) return('A');
  865.         if(num == n - 1)            /* Acknowledge good packet */
  866.         {
  867.         spack('Y',num,0,0);
  868.         numtry = 0;
  869.         return(state);
  870.         }
  871.         else return('A');            /* or "Abort" on bad one */
  872.     }                    /*
  873.  
  874. /* File-Header */
  875.     case 'F':
  876.     {
  877.         if(num != n) return('A');
  878.         getfil(packet);            /* Open the file */
  879.         spack('Y',n,0,0);            /* Acknowledge */
  880.         oldtry = numtry;
  881.         numtry = 0;
  882.         n = (n + 1) % 64;
  883.         return('D');            /* Switch to data state */
  884.     }                    /*
  885.  
  886. /* Break transmission */
  887.     case 'B':
  888.     {
  889.         if(num != n) return('A');
  890.         spack('Y',n,0,0);            /* Say OK */
  891.         return('C');            /* and go to Complete state */
  892.     }
  893.     case FALSE:  return(state);
  894.     default:     return('A');
  895.     }
  896. }                        /* Return
  897. @End<ProgramExample>
  898. @SubSection<Receive-data>
  899. @Begin<ProgramExample>
  900.  
  901. /*@ptxt[This routine is almost the same as Rfile except that it must take
  902.    into account that its previous state could have been either 'F' or
  903.    'D'.  If packets of either type with the previous message number
  904.    are received the previous packet is again acknowledged.  Also if
  905.    the packet is a 'Z' (end of file) the packet is ACKed, any
  906.    necessary file closing is done and the state becomes 'F'.  If a
  907.    data packet is received, bufemp is called to write the characters
  908.    into the target file.  To prevent losing packets while writing, the
  909.    packet should not be ACKed until the characters are written out.]
  910. */
  911.  
  912. char rdata ()
  913.  
  914. {
  915.     int num, len;
  916.  
  917.     if(numtry++ > maxtry) return('A');    /* "Abort" if too many tries */
  918.     switch (rpack(&len,&num,packet))    /* Get a packet */
  919.     {
  920.  
  921. /* Data */
  922.     case 'D':
  923.     {
  924.         if(num != n)        /* Right packet? */
  925.         {                /* No */
  926.         if(oldtry++ > maxtry) return('A'); /* If too many tries */
  927.         if(num == n - 1)        /* Check packet num */
  928.         {
  929.             spack('Y',num,0,0);        /* It's right, ACK it */
  930.             numtry = 0;            /* Reset try counter */
  931.             return(state);
  932.         }
  933.         else return('A');        /* Sorry, wrong number */
  934.         }                    /*
  935.  
  936. /* Data with right packet number */
  937.         bufemp(packet,fptr,len);        /* Write out the packet */
  938.         spack('Y',n,0,0);            /* Right packet, acknowledge */
  939.         oldtry = numtry;
  940.         numtry = 0;
  941.         n = (n + 1) % 64;
  942.         return('D');            /* Remain in data state */
  943.     }                    /*
  944.  
  945. /* File-Send */
  946.     case 'F':
  947.     {
  948.         if(oldtry++ > maxtry) return('A');
  949.         if(num == n - 1)            /* Acknowledge good packet */
  950.         {
  951.         spack('Y',num,0,0);
  952.         numtry = 0;
  953.         return(state);
  954.         }
  955.         else return('A');            /* or "Abort" on bad one */
  956.     }                    /*
  957.  
  958. /* End-of-File */
  959.     case 'Z':
  960.     {
  961.         if(num != n) return('A');
  962.         spack('Y',n,0,0);            /* Say OK */
  963.         close(fptr);            /* Close up the file */        
  964.         return('F');            /* and go to File state */
  965.     }
  966.     case FALSE:  return(state);
  967.     default:     return('A');
  968.     }
  969. }                        /* Return
  970. @End<ProgramExample>
  971. @Section<Utility Routines>
  972. @Begin<ProgramExample>
  973.  
  974. /*@ptxt[Converts a control character to a printable one by adding a space.] */
  975.  
  976. char tochar (ch)
  977.  
  978. char ch;
  979.  
  980. {
  981.     return(ch + ' ');        /* Make sure not a control char */
  982. }
  983.  
  984. /* Undoes tochar. */
  985.  
  986. char unchar (ch)
  987.  
  988. char ch;
  989.  
  990. {
  991.     return(ch - ' ');        /* Restore char */
  992. }
  993.  
  994. /*@ptxt[Turns a control character into a printable character by toggling
  995.    the @i<control> bit (i.e. ^A becomes A and A becomes ^A.)]
  996. */
  997.  
  998. char ctl (ch)
  999.  
  1000. char ch;
  1001.  
  1002. {
  1003.     return(ch ^ 64);        /* Toggle the "Control" bit */
  1004. }                /*
  1005.  
  1006.  
  1007. /*@ptxt[Fill the array data with the appropriate Send-init parameters.] */
  1008.  
  1009. spar (data)
  1010.  
  1011. char data[];
  1012.  
  1013. {
  1014. printf("spar\n");
  1015.     data[0] = tochar(maxpack);        /* Biggest packet I can receive */
  1016.     data[1] = tochar(mytime);        /* When I want to be timed out */
  1017.     data[2] = tochar(mypad);        /* How much padding I need */
  1018.     data[3] = ctl(mypchar);        /* Padding character I want */
  1019.     data[4] = tochar(myeol);        /* End-Of-Line character I want */
  1020.     data[5] = myquote;            /* Quote character I send */
  1021. }                    /*
  1022.  
  1023.  
  1024. /*@ptxt[Get the other host's Send-init parameters.] */
  1025.  
  1026. rpar (data)
  1027.  
  1028. char data[];
  1029.  
  1030. {
  1031. printf("rpar\n");
  1032.     spsiz = unchar(data[0]);        /* Maximum send packet size */
  1033.     timint = unchar(data[1]);        /* When I should time out */
  1034.     pad = unchar(data[2]);        /* Number of pads to send */
  1035.     padchar = ctl(data[3]);        /* Padding character to send */
  1036.     eol = unchar(data[4]);        /* EOL character I must send */
  1037.     quote = data[5];            /* Incoming data quote character */
  1038. }
  1039.